// gofsm是一个简单的，功能强大的FSM实现，与其他FSM实现具有一些不同的功能。
// gofsm的一个特点是它不会保留/保留对象的状态。 当它处理转换时，您必须将当前状态传递给id，
//因此您可以将gofsm视为“无状态”状态机。 这个好处是一个gofsm实例可以用来处理很多对象实例的转换，而不是创建大量的FSM实例。
//对象实例自身保持状态。
//另一个特点是它为Moore和Mealy FSM提供了一个通用接口。 您可以为这两个FSM实现相应的方法（OnExit，Action，OnEnter）。
//第三个有趣的特性是您可以将配置的转换导出到状态图。 一张图片胜过千言万语。
// gofsm的样式执行https://github.com/elimisteve/fsm。
package fsm

import (
	"fmt"
)

// Delegate 用于处理操作。 因为gofsm使用文字值作为事件，状态和动作，所以您需要使用相应的功能处理它们。
//DefaultDelegate是将处理分为三个操作的默认委托实现：OnExit Action，Action和OnEnter Action。 您可以实现不同的代理。
type FsmDelegateInterface interface {
	// HandleEvent handles transitions
	HandleEvent(action string, fromState string, toState string, args map[string]interface{})
}

// StateMachine 可以处理很多对象的转换的FSM。 委托和转换在使用它们之前进行配置。
type FsmStateMachine struct {
	delegate    FsmDelegateInterface
	transitions []Transition
}

// NewStateMachine creates a new state machine.
func NewFsmStateMachine(delegate FsmDelegateInterface, transitions ...Transition) *FsmStateMachine {
	return &FsmStateMachine{delegate: delegate, transitions: transitions}
}

// Trigger 发射一个事件 您必须传递处理对象的当前状态，关于此对象的其他信息可以通过args传递。
func (m *FsmStateMachine) Trigger(currentState string, event string, args map[string]interface{}) Error {
	trans := m.findTransMatching(currentState, event)
	if trans == nil || len(trans) < 1 {
		return smError{event, currentState}
	}
	// beego.Debug(trans)
	for _, t := range trans {
		// beego.Debug(t)
		args["TRANSITION_INSTANCE"] = t.Id
		if t.Action != "" {
			m.delegate.HandleEvent(t.Action, currentState, t.To, args)
		}
	}

	return nil
}

// findTransMatching 根据当前状态和事件获得相应的转换。
func (m *FsmStateMachine) findTransMatching(fromState string, event string) (ts []Transition) {
	// fmt.Println("fromState:: ", fromState)
	// fmt.Println("event:: ", event)
	for _, v := range m.transitions {
		if v.From == fromState && v.Event == event {
			ts = append(ts, v)
		}
	}
	return
}

// Export 将状态图导出到文件中。
func (m *FsmStateMachine) Export(outfile string) error {
	return m.ExportWithDetails(outfile, "png", "dot", "72", "-Gsize=10,5 -Gdpi=200")
}

// ExportWithDetails  使用更多graphviz选项导出状态图。
func (m *FsmStateMachine) ExportWithDetails(outfile string, format string, layout string, scale string, more string) error {
	dot := `digraph StateMachine {
	rankdir=LR
	node[width=1 fixedsize=true shape=circle style=filled fillcolor="darkorchid1" ]
	
	`

	for _, t := range m.transitions {
		link := fmt.Sprintf(`%s -> %s [label="%s | %s"]`, t.FromName, t.ToName, t.EventName, t.ActionName)
		dot = dot + "\r\n" + link
	}

	dot = dot + "\r\n}"
	cmd := fmt.Sprintf("dot -o%s -T%s -K%s -s%s %s", outfile, format, layout, scale, more)

	return system(cmd, dot)
}


// EventProcessor 定义OnExit，Action和OnEnter操作。
type FsmEventProcessor interface {
	// OnExit 动作处理退出状态
	OnExit(fromState string, args map[string]interface{})
	// Action 用于处理转换
	Action(action string, fromState string, toState string, args map[string]interface{})
	// OnEnter Action 进入状态
	OnEnter(toState string, args map[string]interface{})
}

// DefaultDelegate 是默认代理。
//将操作分为三个操作：OnExit，Action和OnEnter。
type FsmDelegate struct {
	P FsmEventProcessor
}

// HandleEvent 实现Delegate接口并将HandleEvent分为三个操作。
func (dd *FsmDelegate) HandleEvent(action string, fromState string, toState string, args map[string]interface{}) {
	if fromState != toState {
		dd.P.OnExit(fromState, args)
	}
	dd.P.Action(action, fromState, toState, args)

	if fromState != toState {
		dd.P.OnEnter(toState, args)
	}
}